#include "bsp_timer.h"
#include "macros.h"

#define TIMER_ONE_SHOT      0x04
#define TIMER_PERIODIC      0x08

static TIMER_STRUCT *timer_head;
static void timer_add(TIMER_STRUCT * timer);

void bsp_timer_init(TIMER_STRUCT *timer,
                    Timeout_Callback cb,
                    uint32_t timeout,
                    uint16_t period,
                    TIMER_TYPE  timer_type)
{
    timer->period           = period;
    timer->timeout          = timeout;
    timer->type             = timer_type;
    timer->cb = cb;
    timer->time_temp   = 0;
    timer->period_temp = period;
    timer->start = false;
    timer->timeout_flag  = false;

    if (period == 0)
        timer->type |= TIMER_PERIODIC;
    else
        timer->type |= TIMER_ONE_SHOT;

    timer_add(timer);
}

void bsp_timer_bind_data(TIMER_STRUCT *timer, void *user_data)
{
    if(CHECK_NULL_PTR(timer)) {
    }
    timer->user_data = user_data;
}

void bsp_timer_start(TIMER_STRUCT *timer)
{
  if(CHECK_NULL_PTR(timer)) {
  }
  timer->start = 1;
}

void bsp_timer_restart(TIMER_STRUCT *timer)
{
    if(CHECK_NULL_PTR(timer)) {
        return;
    }
        timer->start     = 0;
        timer->time_temp = 0;
        timer->period    = timer->period_temp;
        timer->start     = 1;
}

void bsp_timer_pause(TIMER_STRUCT *timer)
{
    if(CHECK_NULL_PTR(timer)) {
        return;
    }
    timer->start = 0;
    timer->time_temp = 0;
}

void bsp_timer_detach(TIMER_STRUCT *timer)
{
    if(CHECK_NULL_PTR(timer)){
        return ;
    }
    TIMER_STRUCT **now_target;
    TIMER_STRUCT *entry;
    for (now_target = &timer_head; *now_target; )
    {
        entry = *now_target;

        if (entry == timer){
            *now_target = entry->next;
            return;
        }
        else
            now_target = &entry->next;
    }
}

void bsp_timer_handler(uint8_t ms)
{
    TIMER_STRUCT *timer  = NULL;
    for (timer = timer_head; timer != NULL; timer = timer->next) {
        if (timer->start)
        {
            timer->time_temp += ms;
            if (timer->time_temp >= timer->timeout)
            {
                timer->time_temp = 0;

                if ((timer->type & TIMER_TYPE_HARDWARE) != 0)
                {
                    if (timer->cb)
                        timer->cb( timer->user_data );

                    if ((timer->type & TIMER_ONE_SHOT) != 0)
                    {
                        --timer->period;
                        if (timer->period == 0)
                            bsp_timer_pause(timer);
                    }
                }
                else
                    timer->timeout_flag = 1;
            }
        }
    }
}

static void timer_add(TIMER_STRUCT * timer)
{
    TIMER_STRUCT *target;
    for (target = timer_head; target != NULL; target = target->next){
        if (target == timer)
            return;
    }
    timer->next = timer_head;
    timer_head  = timer;
}

void bsp_timer_soft_timer_task(void)
{
    TIMER_STRUCT *timer;

    for (timer = timer_head; timer != NULL; timer = timer->next){
        if (timer->timeout_flag) {
            timer->timeout_flag = 0;

            if (timer->cb)
                timer->cb( timer->user_data );

            if ((timer->type & TIMER_ONE_SHOT) != 0){
                --timer->period;
                if (timer->period == 0)
                    bsp_timer_pause(timer);
            }
        }
    }
}